Skip to content

BUG: rocket with a late-starting thrust curve never leaves the rail (#411) - #1085

Merged
Gui-FernandesBR merged 5 commits into
RocketPy-Team:developfrom
wuisabel-gif:bug/rail-takeoff-delayed-thrust
Aug 8, 2026
Merged

BUG: rocket with a late-starting thrust curve never leaves the rail (#411)#1085
Gui-FernandesBR merged 5 commits into
RocketPy-Team:developfrom
wuisabel-gif:bug/rail-takeoff-delayed-thrust

Conversation

@wuisabel-gif

Copy link
Copy Markdown

Pull request type

  • Code changes (bugfix, features)

Checklist

  • Tests for the changes have been added (regression test fails without the fix, passes with it)
  • Lint passes locally (ruff check, ruff format --check; pylint 10/10 on the touched file)
  • Affected flight tests pass locally (tests/unit/simulation/test_flight.py, 56 passed)
  • CHANGELOG.md updated

Current behavior

Fixes #411. A motor whose thrust curve starts at t > 0 (e.g. burn_time=(8, 20)) never lifts the rocket off the rail — the flight stays at (0, 0, 0) with out_of_rail_time == 0.

Root cause (as the issue originally guessed): the rail phase's only time nodes are [t=0, max_time]. With no thrust at t=0 the rocket is stationary, so LSODA — whose max_step defaults to inf — takes one huge step that jumps clean over the burn. The thrust is never sampled and the rocket never accelerates. A normal motor accelerates at t=0, forcing small steps, so it only bites for late-starting curves.

Minimal repro (same rocket and total impulse, only the start time differs):

[thrust starts t=0] out_of_rail_t=0.329  apogee=10329 m
[thrust starts t=8] out_of_rail_t=0.000  apogee=0 m   <- never leaves the pad

New behavior

__setup_phase_time_nodes now forces a solver time node at the motor's ignition and burn-out whenever burn_start_time > 0, so the burn is always sampled. With the fix the delayed case flies identically to the t=0 case, just shifted:

[thrust starts t=8] out_of_rail_t=8.329  apogee=10329 m

The change is guarded to burn_start_time > 0, so ordinary motors (which ignite at t=0) hit none of the new code and are byte-for-byte unaffected — confirmed by the unchanged t=0 result above and the existing flight tests staying green.

Breaking change

  • No

Additional information

Adds test_flight_with_delayed_burn_leaves_rail in tests/integration/simulation/test_flight.py; it asserts a burn_time=(8, 20) rocket leaves the rail after ignition and reaches altitude. It fails on develop and passes with this change.

Closes #411

@wuisabel-gif
wuisabel-gif requested a review from a team as a code owner July 22, 2026 15:58
@wuisabel-gif

wuisabel-gif commented Jul 29, 2026

Copy link
Copy Markdown
Author

@RocketPy-Team Could a maintainer please approve the pending GitHub Actions workflows for this fork-based PR? The runs are waiting for approval before they can start. Thank you!

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.53%. Comparing base (e0ff281) to head (78b5212).
⚠️ Report is 19 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1085      +/-   ##
===========================================
+ Coverage    82.18%   82.53%   +0.34%     
===========================================
  Files          122      128       +6     
  Lines        16355    16556     +201     
===========================================
+ Hits         13441    13664     +223     
+ Misses        2914     2892      -22     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Gui-FernandesBR Gui-FernandesBR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. I reproduced both the bug and the fix, and checked the thing that actually matters for a change inside __setup_phase_time_nodes — whether ordinary motors move.

The bug is real and total. I reverted only rocketpy/simulation/flight.py to develop, kept your test, and it fails exactly as you describe:

assert flight.out_of_rail_time > 8.0
E  assert 0 > 8.0

Ordinary motors are byte-for-byte unaffected. Two rockets with the same total impulse, differing only in start time, printed at full precision:

develop with the fix
t=0 out_of_rail_time 0.3289354306801905 0.3289354306801905
t=0 apogee 12904.559694188314 12904.559694188314
t=0 function_evaluations 2368463 2368463
t=8 out_of_rail_time 0 8.329156434166551
t=8 apogee 0 12904.497507159389
t=8 function_evaluations 9 1953325

The t=0 column is identical down to the solver's function-evaluation count, which is the strongest evidence I could get that the burn_start > 0 guard means ordinary motors take exactly the same path. And function_evaluations = 9 on develop for the delayed case is a nice confirmation of the diagnosis: the solver really does take one enormous step and stop.

The delayed flight also lands on the same apogee as the t=0 one to within 0.06 m, which is what you would expect from the same impulse applied 8 s later.

Also checked: EmptyMotor is constructed with burn_time=1, so its burn_start_time is 0 and it falls into the guard rather than the new branch. The getattr(motor, "burn_start_time", 0) or 0 is defensive but harmless.

Local runs: tests/unit 1848 passed / 16 skipped; tests/unit/simulation/test_flight.py + tests/integration/simulation/test_flight.py 79 passed / 9 skipped; ruff check and ruff format --check clean; pylint rocketpy/simulation/flight.py 10.00/10.

On the Codecov rename — I was ready to ask you to drop it as unrelated scope, and then I looked into it. .github/workflows/codecov.yml is not a workflow at all; it is Codecov's coverage: config sitting in the workflows directory, so Actions has been trying to execute it and failing on every single run since #500 introduced it. Moving it to .codecov.yml is where Codecov documents it and makes that noise go away. Confirmed the rename is content-identical (diff is empty), so keep it — it is a real fix, and worth calling out in the PR body rather than leaving it looking incidental.

Last note: this now has its first green run — all six matrix legs plus Codecov — after #1084 landed and I updated the branch. Every prior run on this PR died in the un-isolated integration step, which is why it never had one.

@Gui-FernandesBR
Gui-FernandesBR merged commit 335834d into RocketPy-Team:develop Aug 8, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants